#include "stdafx.h"
#include "DBFCircleDequeue.h"
#include <memory>
#include <iostream>
#include "MemoryPool.h"
#include "DataDefinition.h"

DBFCircleDequeue::DBFCircleDequeue(unsigned int p_uiEntireBufferSize,unsigned int p_uiElementAmount, unsigned int p_uiElementSize,unsigned int p_uiBeginSequence,unsigned int p_uiEndSequence,unsigned char p_uiEntireDataType, unsigned char p_uiIncrementDataType)
	:CircleDequeueBase(p_uiEntireBufferSize,p_uiElementAmount, p_uiElementSize, p_uiBeginSequence, p_uiEndSequence, p_uiEntireDataType, p_uiIncrementDataType)
{
}

bool DBFCircleDequeue::WriteData(char* p_szBuffer, unsigned int p_uiBufferSize)
{
	EnterCriticalSection(&m_csLock);

	// Store local date
	DateTime clsLocalDate;
	clsLocalDate.SetToCurrentDate();
	m_clsLastDate = clsLocalDate;
	
	if(0 == (m_uiCount % 5))
	{// Entire
		// Compress data and copy to compressed buffer
		m_uiComCacheLength = m_uiComCacheSize;
		CircleDequeueBase::CompressData(m_szCompressCache,m_uiComCacheLength,p_szBuffer,p_uiBufferSize,m_ucEntireDataType);
		memcpy(m_pszSendBuffer,m_szCompressCache,m_uiComCacheLength);
		m_uiSendBufferLength = m_uiComCacheLength;
		if(false == m_vecRookieSocketList.empty())
		{
			if(false == CircleDequeueBase::Notify(m_vecRookieSocketList,m_pszSendBuffer,m_uiSendBufferLength))
			{
				return false;
			}
		}

		m_uiCount = 0;
		m_uiSendBufferIndex++;
	}
	
	// Increment
	// Compare data from bufferAddress and m_pszEntireBuffer to find updated data and store into queue element
	int iRet = CircleDequeueBase::CompareData(p_szBuffer,p_uiBufferSize);
	if(iRet > 0)
	{
		//LOG(INFO) << "Success when comparing data. File type : " << (unsigned int)m_uiEntireDataType;
		CircleDequeueBase::Notify(m_vecAlivedSocketList,m_pstCurrentAddr->m_szElementAddr,m_pstCurrentAddr->m_uiAllocedSize);
	}
	else if (iRet < 0)
	{
		//LOG(INFO) << "Error when comparing data. File type : " << (unsigned int)m_uiEntireDataType;
		int iError = GetLastError();
		if ((iError == CD_WARNING_LEN_DISMATCH) || (iError == CD_WARNING_CONTENT_DISMATCH))
		{
			// Send entire package
			m_uiComCacheLength = m_uiComCacheSize;
			CircleDequeueBase::CompressData(m_szCompressCache,m_uiComCacheLength,p_szBuffer,p_uiBufferSize,m_ucEntireDataType);
			memcpy(m_pszSendBuffer,m_szCompressCache,m_uiComCacheLength);
			m_uiSendBufferLength = m_uiComCacheLength;
			CircleDequeueBase::Notify(m_vecAlivedSocketList,m_pszSendBuffer,m_uiSendBufferLength);
		}
	}

	//LOG(INFO) << "Alive socket count : " << m_vecAlivedSocketList.size() << ". File type : " << (unsigned int)m_uiEntireDataType;
	memcpy(m_pszEntireBuffer,p_szBuffer,p_uiBufferSize);
	m_uiEntireBufferLength = p_uiBufferSize;
	m_uiCount++;
	m_vecAlivedSocketList.insert(m_vecAlivedSocketList.end(),m_vecRookieSocketList.begin(),m_vecRookieSocketList.end());
	m_vecRookieSocketList.clear();

	LeaveCriticalSection(&m_csLock);
	return true;
}

bool DBFCircleDequeue::WriteData(char* p_szBufferAddress, unsigned int p_uiBufferSize, unsigned char p_cType, bool p_bIsResend)
{
	EnterCriticalSection(&m_csLock);

	// Store current local date
	DateTime clsLocalDate;
	clsLocalDate.SetToCurrentDate();
	m_clsLastDate = clsLocalDate;

	if((p_cType == m_ucEntireDataType) || (p_cType == (m_ucEntireDataType | 0x80)))
	{
		DataHead* pstDataHead = (DataHead*)m_pszSendBuffer;
		pstDataHead->m_uiDataLength = p_uiBufferSize;
		pstDataHead->m_ucDataType = p_cType;
		
		// Copy source buffer to compressed buffer and add data head
		memcpy(m_pszSendBuffer + sizeof(DataHead),p_szBufferAddress,p_uiBufferSize);
		m_uiSendBufferLength = p_uiBufferSize + sizeof(DataHead);
		if (p_cType == m_ucEntireDataType) // Uncompressed entire data
		{
			// Copy source buffer to entire buffer
			memset(m_pszEntireBuffer,0,m_uiEntireBufferSize);
			memcpy(m_pszEntireBuffer,p_szBufferAddress,p_uiBufferSize);
			m_uiEntireBufferLength = p_uiBufferSize;
		}
		else // Compressed entire data
		{ 
			// Uncompress data to m_pszEntireBuffer in order to combine with following increment packet
			unsigned int uiBufferSize = m_uiComCacheSize;
			CircleDequeueBase::UncompressData(m_szCompressCache,uiBufferSize,p_szBufferAddress,p_uiBufferSize);
			m_uiComCacheLength = uiBufferSize;
			
			// Copy source buffer to entire buffer
			memset(m_pszEntireBuffer,0,m_uiEntireBufferSize);
			memcpy(m_pszEntireBuffer,m_szCompressCache,uiBufferSize);
			m_uiEntireBufferLength = m_uiComCacheLength;
		}

		if(true == p_bIsResend)
		{
			// Send out packet to client socket
			if(false == CircleDequeueBase::Notify(m_vecAlivedSocketList,m_pszSendBuffer,m_uiSendBufferLength))
			{
				LeaveCriticalSection(&m_csLock);
				return false;
			}

			if(false == CircleDequeueBase::Notify(m_vecRookieSocketList,m_pszSendBuffer,m_uiSendBufferLength))
			{
				LeaveCriticalSection(&m_csLock);
				return false;
			}

			// Move client from rookie socket list to alive socket list
			m_vecAlivedSocketList.insert(m_vecAlivedSocketList.end(),m_vecRookieSocketList.begin(),m_vecRookieSocketList.end());
			m_vecRookieSocketList.clear();
		}
		else
		{
			if(false == m_vecRookieSocketList.empty())
			{
				// Send out packet to client socket
				if(false == CircleDequeueBase::Notify(m_vecRookieSocketList,m_pszSendBuffer,m_uiSendBufferLength))
				{
					LeaveCriticalSection(&m_csLock);
					return false;
				}

				// Move client from rookie socket list to alive socket list
				m_vecAlivedSocketList.insert(m_vecAlivedSocketList.end(),m_vecRookieSocketList.begin(),m_vecRookieSocketList.end());
				m_vecRookieSocketList.clear();
			}
		}
	}
	else
	{
		if (p_cType == m_ucIncrementDataType) // Uncompressed increment data
		{
			// Copy data to cache
			memcpy(m_szCompressCache,p_szBufferAddress,p_uiBufferSize);
			m_uiComCacheLength = p_uiBufferSize;
		} 
		else // Compressed increment data
		{
			// Uncompress bufferAddress and combine with m_pszEntireBuffer
			unsigned int uiCacheSize = m_uiComCacheSize;
			CircleDequeueBase::UncompressData(m_szCompressCache,uiCacheSize,p_szBufferAddress,p_uiBufferSize);
			m_uiComCacheLength = uiCacheSize;
		}

		CircleDequeueBase::CombineData(m_szCompressCache,m_uiComCacheLength);
		if(0 == (m_uiCount % 5))
		{
			//Compress m_pszEntireBuffer to m_pszComEntireBuffer
			m_uiComCacheLength = m_uiComCacheSize;
			CircleDequeueBase::CompressData(m_szCompressCache,m_uiComCacheLength,m_pszEntireBuffer,m_uiEntireBufferLength,m_ucEntireDataType);
			memcpy(m_pszSendBuffer,m_szCompressCache,m_uiComCacheLength);
			m_uiSendBufferLength = m_uiComCacheLength;
			m_uiSendBufferIndex++;
			m_uiCount = 0;
		}

		// Copy buffer into queue
		DataHead* pstDataHead = (DataHead*)m_pszCache;
		pstDataHead->m_uiDataLength = p_uiBufferSize;
		pstDataHead->m_ucDataType = p_cType;
		memcpy(m_pszCache + sizeof(DataHead),p_szBufferAddress,p_uiBufferSize);
		m_uiCacheLength = p_uiBufferSize + sizeof(DataHead);
		CircleDequeueBase::WriteElement(m_pszCache,p_uiBufferSize+ sizeof(DataHead));

		// Notify clients
		CircleDequeueBase::Notify(m_vecAlivedSocketList, m_pstCurrentAddr->m_szElementAddr, m_pstCurrentAddr->m_uiAllocedSize);
		m_uiCount++;
	}

	LeaveCriticalSection(&m_csLock);
	return true;
}